/**
 * 
 */
package org.dragonfire.dasm.instr;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;

import org.dragonfire.dasm.CompileException;
import org.dragonfire.dasm.DASM;
import org.dragonfire.parser.Equation;
import org.dragonfire.util.DMath;
import org.dragonfire.util.DString;

/**
 * @author dandroid
 * @date Nov 30, 2006
 */
public class InstructionTable {

	private HashMap<String, TreeSet<Register>> registers = new HashMap<String, TreeSet<Register>>();
	private HashMap<String, ArrayList<CommandDef>> commandDefs = new HashMap<String, ArrayList<CommandDef>>();
	private DASM comp;

	public InstructionTable(DASM compiler) {
		comp = compiler;
	}

	public void addCommand(CommandDef c) {
		if (c == null)
			return;
		String name = c.getName().toLowerCase();
		c.setCompiler(getCompiler());
		if (!commandDefs.containsKey(name))
			commandDefs.put(name, new ArrayList<CommandDef>());
		commandDefs.get(name).add(c);
		Collections.sort(commandDefs.get(name));
	}

	/**
	 * Returns the number of commands that have been added to this table. All
	 * overloaded commands count individually and not as one.
	 * 
	 * @return The number of commands loaded in this table.
	 */
	public int getCommandCount() {
		int count = 0;
		for (String key : commandDefs.keySet()) {
			count += commandDefs.get(key).size();
		}
		return count;
	}

	public CommandDef getCommand(String name, String[] params) {
		if (name == null)
			throw new IllegalArgumentException("The name parameter may not be null");
		name = name.toLowerCase();
		if (!commandDefs.containsKey(name))
			throw new CompileException("ERROR_UNDEFINED_COMMAND", name);
		for (CommandDef c : commandDefs.get(name)) {
			if (isOverloadMatch(c.getParamFormats(), params))
				return c;
		}
		throw new CompileException("ERROR_COMMAND_INVALID_PARAMS", name);
	}

	public void addRegister(Register r) {
		if (r == null)
			return;
		if (!registers.containsKey(r.getName().toLowerCase()))
			registers.put(r.getName().toLowerCase(), new TreeSet<Register>());
		registers.get(r.getName().toLowerCase()).add(r);
	}

	public DASM getCompiler() {
		return comp;
	}

	public boolean isRegister(String name) {
		return getRegister(name) != null;
	}

	public boolean isExactMatch(String[] params, String[] subParams) {
		if (params.length != subParams.length) {
			return false;
		}
		params = CommandDef.validateParamTypes(params);
		subParams = CommandDef.validateParamTypes(subParams);
		for (int i = 0; i < params.length; i++) {
			String[] param = DString.splitTopLevel(subParams[i], ':');
			String[] cmdParam = DString.splitTopLevel(params[i], ':');
			char type = param[0].charAt(0);
			if (type != cmdParam[0].charAt(0))
				return false;
			switch (cmdParam[0].charAt(0)) {
				case 'L':
					if (param[1].equalsIgnoreCase(cmdParam[1]))
						continue;
					return false;
				case 'P':
					if (DMath.isInteger(param[1])) {
					}
					if (param[1].matches("\\(.*\\)") && getRegister(param[1], new Integer(cmdParam[2])) != null)
						continue;
					return false;
				case 'M':
					if ((type == 'L' || type == 'M' || type == 'P' || type == 'V' || type == 'O')
							&& param[1].matches("\\(.*\\)"))
						continue;
					return false;
				case 'O':
					if (type == 'O' && param[1].equals(cmdParam[1]) && param[2].equals(cmdParam[2]))
						continue;
					if (type == 'V' && param[1].matches("\\(.*\\+.*\\)")) {
						String reg = param[1].replaceAll("\\((.*)\\+.*\\)", "$1");
						if (getRegister(reg, new Integer(cmdParam[2])) != null)
							continue;
					}
					return false;
				case 'R':
					switch (type) {
						case 'R':
							if ((new Integer(param[1])).equals(new Integer(cmdParam[1]))
									&& (new Integer(param[2])).equals(new Integer(cmdParam[2])))
								continue;
							return false;
						case 'L':
							if (getRegister(param[1], new Integer(cmdParam[2])) != null)
								continue;
							return false;
						default:
							return false;
					}
				case 'V':
					continue;
				case 'S':
					if (type != 'S' && type != 'L')
						return false;
					continue;
				default: // should never get here, or validateParamTypes() is
					// broken
					throw new IllegalArgumentException("Illegal param type '" + type + "'");
			}
		}
		return true;
	}

	public boolean isOverloadMatch(String[] params, String[] subParams) {
		if (params.length != subParams.length) {
			return false;
		}
		params = CommandDef.validateParamTypes(params);
		for (int i = 0; i < params.length; i++) {
			String param = DString.trim(subParams[i]);
			String[] cmdParam = DString.splitTopLevel(params[i], ':');
			switch (cmdParam[0].charAt(0)) {
				case 'L':
					if (param.equalsIgnoreCase(cmdParam[1]))
						continue;
					return false;
				case 'P':
					if (param.matches("\\(.*\\)")) {
						String ptr = param.substring(1, param.length() - 1);
						if (ptr.matches(DASM.REGISTER_REGEX) && getRegister(ptr, new Integer(cmdParam[2])) != null)
							continue;
					}
					return false;
				case 'M':
					if (param.matches("\\(.*\\)"))
						continue;
					return false;
				case 'O':
					if (param.matches("\\(.*\\+.*\\)")) {
						String reg = param.replaceAll("\\((.*)\\+.*\\)", "$1");
						if (getRegister(reg, new Integer(cmdParam[2])) != null)
							continue;
					}
					return false;
				case 'R':
					if (getRegister(param, new Integer(cmdParam[2])) != null)
						continue;
					return false;
				case 'V':
					continue;
				case 'S':
					if (Equation.isString(param))
						continue;
					return false;
				default: // should never get here, or validateParamTypes() is
					// broken
					throw new IllegalArgumentException("Illegal param type '" + cmdParam[0] + "'");
			}
		}
		return true;
	}

	public Register getRegister(String name) {
		if (name == null)
			throw new IllegalArgumentException("name may not be null");
		String name2 = name.toLowerCase();
		if (!registers.containsKey(name2))
			return null;
		return registers.get(name).first();
	}

	public Register getRegister(String name, int classID) {
		if (name == null)
			throw new IllegalArgumentException("name may not be null");
		String name2 = name.toLowerCase();
		if (!registers.containsKey(name2))
			return null;
		TreeSet<Register> regs = registers.get(name2);
		for (Register r : regs) {
			if (r.getClassID() == classID)
				return r;
		}
		return null;
	}

	public void addRegister(String name, int classID, String bits) {
		addRegister(new Register(name, classID, bits));
	}
}
